package com.douxg.fastCoding.web.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.douxg.fastCoding.enjoy.EnjoyManager;
import com.douxg.fastCoding.spring.consts.BizCode;
import com.douxg.fastCoding.spring.exception.BizRuntimeException;
import com.douxg.fastCoding.web.consts.FunctionEnum;
import com.douxg.fastCoding.web.dto.EnjoyDto;
import com.douxg.fastCoding.web.entity.Enjoy;
import com.douxg.fastCoding.web.mapper.EnjoyMapper;
import com.douxg.fastCoding.web.service.EnjoyService;
import com.douxg.fastCoding.web.vo.ElSelectVo;
import com.douxg.fastCoding.web.vo.EnjoyVo;
import com.jfinal.template.Template;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Auther
 * @Date 2020-07-31 15:06
 */
@Service
public class EnjoyServiceImpl extends ServiceImpl<EnjoyMapper, Enjoy> implements EnjoyService {

    @Resource
    private EnjoyManager enjoyManager;

    /**
     * 根据通用条件分页查询，带统计总数量和默认排序
     *
     * @param enjoyDto dto类
     * @param current  当前页码
     * @param size     每页数量
     * @return IPage<EnjoyVo>
     */
    @Override
    public IPage<EnjoyVo> page(EnjoyDto enjoyDto, Integer current, Integer size) {
        return page(enjoyDto, current, size, false);
    }

    /**
     * 根据通用条件分页查询
     *
     * @param enjoyDto dto类
     * @param current  当前页码
     * @param size     每页数量
     * @param queryOne 是否查询一个，如果是则不统计总数量且不排序
     * @return IPage<EnjoyVo>
     */
    private IPage<EnjoyVo> page(EnjoyDto enjoyDto, Integer current, Integer size, boolean queryOne) {
        Page<Map<String, Object>> paramsPage = new Page<>();
        paramsPage.setCurrent(current);
        paramsPage.setSize(size);
        QueryWrapper<Enjoy> queryWrapper;
        if (queryOne) {
            paramsPage.setSearchCount(false);
            queryWrapper = getPageQueryWrapper(enjoyDto, false);
        } else {
            paramsPage.setSearchCount(true);
            queryWrapper = getPageQueryWrapper(enjoyDto, true);
        }
        Page<Map<String, Object>> dbPage = baseMapper.selectMapsPage(paramsPage, queryWrapper);
        List<EnjoyVo> list = coverToVos(dbPage.getRecords());
        Page<EnjoyVo> resultPage = new Page<>();
        resultPage.setTotal(dbPage.getTotal());
        resultPage.setCurrent(dbPage.getCurrent());
        resultPage.setSize(dbPage.getSize());
        resultPage.setRecords(list);
        return resultPage;
    }

    /**
     * 根据通用条件获取一条数据，基于分页实现，查询出多条记录会刨异常
     *
     * @param enjoyDto dto类
     * @return EnjoyVo
     */
    @Override
    public EnjoyVo selectOne(EnjoyDto enjoyDto) {
        IPage<EnjoyVo> page = page(enjoyDto, 1, 2, true);
        List<EnjoyVo> records = page.getRecords();
        if (records.size() == 1) {
            return records.get(0);
        } else if (records.size() > 1) {
            throw new RuntimeException("查询数据不唯一");
        }
        return null;
    }

    /**
     * 根据通用条件获取一条数据，基于分页实现，查询出多条记录取第一条
     *
     * @param enjoyDto dto类
     * @return EnjoyVo
     */
    @Override
    public EnjoyVo selectOneNoEx(EnjoyDto enjoyDto) {
        IPage<EnjoyVo> page = page(enjoyDto, 1, 1, true);
        List<EnjoyVo> records = page.getRecords();
        if (records.size() > 0) {
            return records.get(0);
        }
        return null;
    }

    /**
     * 根据通用条件查询一批数据
     * 注意数据量过大会有oom的风险，如果不能确定安全数量建议使用分页接口
     *
     * @param enjoyDto dto类
     * @return List<EnjoyVo>
     */
    @Override
    public List<EnjoyVo> list(EnjoyDto enjoyDto) {
        QueryWrapper<Enjoy> queryWrapper = getPageQueryWrapper(enjoyDto, false);
        List<Map<String, Object>> maps = baseMapper.selectMaps(queryWrapper);
        List<EnjoyVo> list = coverToVos(maps);
        return list;
    }

    /**
     * 插入数据
     *
     * @param enjoyDto dto类
     * @return boolean
     */
    @Override
    public boolean insert(EnjoyDto enjoyDto) {
        LocalDateTime now = LocalDateTime.now();
        enjoyDto.setCreateTime(now);
        enjoyDto.setUpdateTime(now);
        return save(enjoyDto);
    }

    /**
     * 通过主键更新数据
     * 先根据主键查询，若不存在抛出异常
     *
     * @param enjoyDto dto类
     * @return boolean
     */
    @Override
    public boolean updateById(EnjoyDto enjoyDto) {
        Enjoy enjoy = getById(enjoyDto.getId());
        if (enjoy == null || enjoy.getName() == null) {
            throw new RuntimeException("数据不存在");
        }
        enjoy = enjoyDto;
        LocalDateTime now = LocalDateTime.now();
        enjoy.setUpdateTime(now);
        return updateById(enjoy);
    }

    /**
     * 通用查询条件，修改会造成其它功能失效，如需修改，建议新增接口
     *
     * @param enjoyDto dto类
     * @param orderBy  是否开启默认排序
     * @return QueryWrapper<Enjoy>
     */
    private QueryWrapper<Enjoy> getPageQueryWrapper(EnjoyDto enjoyDto, boolean orderBy) {
        QueryWrapper<Enjoy> queryWrapper = new QueryWrapper();
        Long id = enjoyDto.getId();
        if (id != null) {
            queryWrapper.eq(EnjoyMapper.ID, id);
        }
        String name = enjoyDto.getName();
        if (name != null) {
            queryWrapper.eq(EnjoyMapper.NAME, name);
        }
        String functionName = enjoyDto.getFunctionName();
        if (functionName != null) {
            queryWrapper.eq(EnjoyMapper.FUNCTION_NAME, functionName);
        }
        String groupName = enjoyDto.getGroupName();
        if (groupName != null) {
            queryWrapper.eq(EnjoyMapper.GROUP_NAME, groupName);
        }
        String content = enjoyDto.getContent();
        if (content != null) {
            queryWrapper.eq(EnjoyMapper.CONTENT, content);
        }
        Integer status = enjoyDto.getStatus();
        if (status != null) {
            queryWrapper.eq(EnjoyMapper.STATUS, status);
        }
        LocalDateTime createTime = enjoyDto.getCreateTime();
        if (createTime != null) {
            queryWrapper.eq(EnjoyMapper.CREATE_TIME, createTime);
        }
        String startCreateTimeFmt = enjoyDto.getStartCreateTimeFmt();
        if (StrUtil.isNotBlank(startCreateTimeFmt)) {
            queryWrapper.ge(EnjoyMapper.CREATE_TIME, startCreateTimeFmt);
        }
        String endCreateTimeFmt = enjoyDto.getEndCreateTimeFmt();
        if (StrUtil.isNotBlank(endCreateTimeFmt)) {
            queryWrapper.le(EnjoyMapper.CREATE_TIME, endCreateTimeFmt);
        }
        LocalDateTime updateTime = enjoyDto.getUpdateTime();
        if (updateTime != null) {
            queryWrapper.eq(EnjoyMapper.UPDATE_TIME, updateTime);
        }
        queryWrapper.orderByDesc(orderBy, "create_time");
        return queryWrapper;
    }

    /**
     * map转VO
     *
     * @param map map
     * @return EnjoyVo
     */
    public static EnjoyVo coverToVo(Map<String, Object> map) {
        if (map == null) {
            return null;
        }
        EnjoyVo enjoyVo = new EnjoyVo();
        BeanUtil.fillBeanWithMap(map, enjoyVo, true, CopyOptions.create().setIgnoreCase(true));
        return enjoyVo;
    }

    /**
     * map集合转VO
     *
     * @param maps maps
     * @return List<EnjoyVo>
     */
    public static List<EnjoyVo> coverToVos(List<Map<String, Object>> maps) {
        List<EnjoyVo> list = new ArrayList<>();
        if (maps == null || maps.size() == 0) {
            return list;
        }
        for (Map<String, Object> map : maps) {
            list.add(coverToVo(map));
        }
        return list;
    }

    /**
     * map集合转VO
     *
     * @param enjoyVo vo类
     * @return EnjoyDto
     */
    public static EnjoyDto coverVoToDto(EnjoyVo enjoyVo) {
        EnjoyDto enjoyDto = new EnjoyDto();
        BeanUtil.copyProperties(enjoyVo, enjoyDto);
        return enjoyDto;
    }

    @Override
    public JSONObject selectFunctionTypes(String functionName) {
        List<Enjoy> maps = baseMapper.selectFunctionTypes();
        JSONObject jsonObject = new JSONObject();
        List<ElSelectVo> functions = new ArrayList<>();
        Map<String, List<ElSelectVo>> functionTypes = new HashMap<>();
        for (Enjoy enjoy : maps) {
            String function = enjoy.getFunctionName();
            String group = enjoy.getGroupName();
            List<ElSelectVo> types = functionTypes.get(function);
            if (types == null) {
                types = new ArrayList<>();
                functions.add(ElSelectVo.createByValue(function));
                functionTypes.put(function, types);
            }
            types.add(ElSelectVo.createByValue(group));
        }
        for (FunctionEnum functionEnum : FunctionEnum.values()) {
            String name = functionEnum.getName();
            List<String> groups = functionEnum.getGroups();
            List<ElSelectVo> elSelectVos = ElSelectVo.createListByValue(groups);
            if (functions.contains(ElSelectVo.createByValue(name))) {
                List<ElSelectVo> groups2 = functionTypes.get(name);
                for (ElSelectVo elSelectVo : elSelectVos) {
                    if (!groups2.contains(elSelectVo)) {
                        groups2.add(elSelectVo);
                    }
                }
            } else {
                functions.add(ElSelectVo.createByValue(name));
                functionTypes.put(name, elSelectVos);
            }
        }
        if (StrUtil.isBlank(functionName)) {
            jsonObject.put("functions", functions);
            jsonObject.put("functionTypes", functionTypes);
        } else {
            jsonObject.put("groups", functionTypes.get(functionName));
        }
        return jsonObject;
    }

    @Override
    public String run(EnjoyDto enjoyDto, String content, Map kv) {
        String name = enjoyDto.getName();
        if (StrUtil.isEmpty(content) && StrUtil.isEmpty(name)) {
            throw new BizRuntimeException().setBizCode(BizCode.PARAM_ISNULL);
        }
        Template template;
        if (StrUtil.isNotEmpty(content)) {
            template = enjoyManager.getTemplateByContent(content);
        } else {
            template = enjoyManager.getTemplate(enjoyDto);
        }
        return template.renderToString(kv);
    }

    @Override
    public int renew(EnjoyDto enjoyDto) {
        Enjoy byId = getById(enjoyDto.getId());
        if (byId == null) {
            throw new BizRuntimeException(BizCode.DB_NO_DATA);
        }
        String functionName = byId.getFunctionName();
        if (FunctionEnum.CODE.getName().equals(functionName)) {
            String groupName = byId.getGroupName();
            String name = byId.getName();
            String filePath = "enjoy/" + groupName + "/" + name + "." + byId.getFileType() + ".txt";
            ClassPathResource classPathResource = new ClassPathResource(filePath);
            if (classPathResource.exists()) {
                String content = FileUtil.readUtf8String(classPathResource.getPath());
                content = content.replaceAll("\r\n", "\n");
                byId.setContent(content);
                if (updateById(byId)) {
                    return 1;
                } else {
                    return -1;
                }
            }
        }
        return 0;
    }

    @Value("${enjoy.template.enjoy:./config/enjoy}")
    private String enjoyPath;

    @Override
    public int allRenew() {
        File file = new File(enjoyPath);
        if (!file.exists()) {
            ClassPathResource classPathResource = new ClassPathResource("enjoy");
            if (classPathResource.exists()) {
                try {
                    file = classPathResource.getFile();
                } catch (IOException e) {
                    throw new BizRuntimeException(BizCode.OTHER, e);
                }
            }
        }
        if (file.exists()) {
            File[] files = file.listFiles();
            String functionName = FunctionEnum.CODE.getName();
            List<Boolean> resultList = new ArrayList<>();
            long i = 0L;
            for (File groupFile : files) {
                String groupName = groupFile.getName();
                for (File nameFile : groupFile.listFiles()) {
                    if (nameFile.isFile()) {
                        String[] split = nameFile.getName().split("\\.");
                        String name = split[0];
                        EnjoyDto enjoyDto = new EnjoyDto();
                        enjoyDto.setName(name);
                        enjoyDto.setFileType(split[1]);
                        enjoyDto.setGroupName(groupName);
                        enjoyDto.setFunctionName(functionName);
                        EnjoyVo enjoyVo = selectOne(enjoyDto);
                        String content = FileUtil.readUtf8String(nameFile);
                        content = content.replaceAll("\r\n", "\n");
                        if (enjoyVo != null && enjoyVo.getId() != null) {
                            enjoyVo.setContent(content);
                            enjoyVo.setUpdateTime(LocalDateTime.now());
                            resultList.add(updateById(enjoyVo));
                        } else {
                            enjoyDto.setId(++i);
                            enjoyDto.setContent(content);
                            resultList.add(insert(enjoyDto));
                        }
                    }
                }
            }
            int success = 0;
            for (Boolean aBoolean : resultList) {
                if (aBoolean) {
                    success++;
                }
            }
            int size = resultList.size();
            if (size > 0) {
                if (size == success) {
                    return 1;
                } else {
                    return success - size;
                }
            }
        }
        return 0;
    }

    @Override
    public List<String> groups(EnjoyDto enjoyDto) {
        return this.baseMapper.groups(enjoyDto);
    }
}
